///\file glapplication.hh
///\brief contient les déclarations génériques nécessaires 
///pour créer une application OpenGL
///\author Allan BLANCHARD
#ifndef _GLAPPLICATION
#define _GLAPPLICATION

#include <GL/glut.h>
#include <GL/gl.h>
#include <GL/glu.h>

class GLApplication;

///\class GLAppCreator
///\brief Cette classe gère la création générique d'une application OpenGL.
///
///Il est nécessaire d'en hériter en suivant le modèle suivant pour la création 
///d'une application du type "mygl" :\n
///class "myglappcreator" : public GLAppCreator{\n
///  friend class GLApplication;\n
///\n
///public :\n
///"myglappcreator"(unsigned int width, unsigned int height, const char* name) :\n
///    myglappcreator(width, height, name) {}\n
///\n
///  "mygl"* getInstanceOfMyGL(){\n
///    return static_cast<"mygl"*>(GLApplication::create(*this));\n
///  }\n
///\n
///private :\n
///  GLApplication* create(){\n
///    return new "mygl"(w, h, name);\n
///  }\n
///};\n
class GLAppCreator{
  friend class GLApplication;  
protected:
  unsigned int w; ///< Largeur de la fenêtre
  unsigned int h; ///< Hauteur de la fenêtre
  const char* name; ///< Titre de la fenêtre

  ///\brief Constructeur
  ///\param width largeur de fenêtre à créer
  ///\param height hauteur de la fenêtre à créer
  ///\param name nom à appliquer à la fenêtre OpenGL
  GLAppCreator(unsigned int width, unsigned int height, const char* name) :
    w(width), h(height), name(name){}

  ///\brief
  ///permet de créer la nouvelle application OpenGL
  ///GLApplication garantit son unicité
  virtual GLApplication* create() = 0;
};

///\class GLApplication
///\brief classe générique pour la création d'application OpenGL
///
///Le modèle doit être hérité de cette manière :\n
///class "myglapp" : public GLApplication{\n
///  friend class "myglappcreator";\n
///\n
///  "myglapp"(unsigned int width, unsigned int height, const char* name)\n
///    : GLApplication(width, height, name){}\n
///\n
///public :\n
///  void display();\n
///  void idle();\n
///  void keynormal(unsigned char key, int x, int y);\n
///  void keyspecial(int key, int x, int y);\n
///  void mouseclick(int b, int e, int x, int y);\n
///  void mousemot(int x, int y);\n
///  void passivemousemotion(int x, int y);\n
///};\n

class GLApplication{
private:
  static GLApplication* gla; ///< Instance de classe OpenGL
  static unsigned int nbinstance; ///< nombre d'occurrence d'instance fournies

protected:
  unsigned int w; ///< Largeur de la fenêtre
  unsigned int h; ///< Hauteur de la fenêtre
  const char* name; ///< Nom de l'application

  ///\brief
  ///Constructeur générique d'application
  ///\param width largeur de fenêtre
  ///\param height hauteur de fenêtre
  ///\param name nom de la fenêtre
  GLApplication(unsigned int width, unsigned int height, const char* name){
    w = width;
    h = height;
    this->name = name;

    glutInitWindowSize(w,h);
    glutCreateWindow(name);
  }

public :
  ///\brief
  ///Permet la création d'une application et garantit son unicité
  ///\param a GLAppCreator correspondant au type d'application à créer
  ///
  ///Exemple : 
  ///"myglappcreator" a(w,h,name);
  ///create(a);
  static GLApplication* create(GLAppCreator &a){
    if(gla){
      nbinstance++;
      return gla;
    }
    else{
      nbinstance++;
      gla = a.create();
      return gla;
    }
  }

  ///\brief Destructeur
  virtual ~GLApplication(){
    nbinstance--;
    if(nbinstance == 0){
      delete gla;
      gla = 0;
    }
  }

  ///\brief fonction d'init OpenGL
  void init();

  ///\brief fonction d'affichage OpenGL (à surcharger)
  virtual void display() = 0;

  ///\brief fonction idle OpenGL (à surcharger)
  virtual void idle() = 0;

  ///\brief fonction de clavier basique (à surchager)
  ///\param key touche appuyée
  ///\param x position sur x
  ///\param y position sur y
  virtual void keynormal(unsigned char key, int x, int y) = 0;

  ///\brief fonction de clavier special (à surcharger)
  ///\param key touche appuyée
  ///\param x position sur x
  ///\param y position sur y
  virtual void keyspecial(int key, int x, int y) = 0;

  ///\brief fonction de gestion des clics souris
  ///\param b bouton activé
  ///\param e etat du bouton
  ///\param x position sur x
  ///\param y position sur y
  virtual void mouseclick(int b, int e, int x, int y) = 0;

  ///\brief test de déplacement de la souris
  ///\param x deplacement sur x
  ///\param y deplacement sur y
  virtual void mousemot(int x, int y) = 0;
  
  ///\brief test de déplacement souris sans clic
  ///\param x deplacement sur x
  ///\param y deplacement sur y
  virtual void passivemousemotion(int x, int y) = 0;

  ///\brief fonction d'arrêt
  virtual void exit() = 0;

  ///\brief redimensionnement de la fenetre
  ///\param width largeur
  ///\param height hauteur
  void reshape(int width, int height);
  
  ///\brief Activation du rendu
  void render();

  ///\brief Afficher le framerate dans le titre
  void framerate();
private:
  ///\brief fonction de bind init
  static void pinit();

  ///\brief fonction de bind display
  static void pdisplay();

  ///\brief fonction de bind idle
  static void pidle();

  ///\brief fonction de bind clavier
  ///\param key touche appuyée
  ///\param x position sur x
  ///\param y position sur y
  static void pkeynormal(unsigned char key, int x, int y);

  ///\brief fonction de bind clavier special
  ///\param key touche appuyée
  ///\param x position sur x
  ///\param y position sur y
  static void pkeyspecial(int key, int x, int y);

  ///\brief fonction de bind clic souris
  ///\param b bouton activé
  ///\param e etat du bouton
  ///\param x position sur x
  ///\param y position sur y
  static void pmouseclick(int b, int e, int x, int y);

  ///\brief fonction de bind deplacement souris
  ///\param x deplacement sur x
  ///\param y deplacement sur y
  static void pmousemot(int x, int y);

  ///\brief fonction de bind deplacement souris sans click
  ///\param x deplacement sur x
  ///\param y deplacement sur y
  static void ppassivemousemot(int x, int y);
  
  ///\brief fonction de bind redimensionnement
  ///\param width largeur
  ///\param height hauteur
  static void preshape(int width, int height);
  
  ///\brief fonction de bind exit
  static void pexit();

};

#endif
